import {mapBase} from "../base/common/Constants";

export class AStarBase {
    x: number = 0;   //  x 坐标/单位
    y: number = 0;   //  y 坐标/单位
    type: number = 0;  // 格子类型
    F: number = 0;     // 此格子总代价（F=G+H）
    G: number = 0;     // 此格子到起点的代价（G）
    H: number = 0;     // 此格子到终点的估算代价（H）
    parentNode?: AStarBase = null;   // 父节点（追溯节点，不是实际意义上的父节点）

    constructor(x, y, type) {
        this.x = x;
        this.y = y;
        this.type = type;
        this.F = 0;
        this.G = 0;
        this.H = 0;
        this.parentNode = null;
    }
}

export default class AStar {

    openList: AStarBase[] = [];
    closeList: AStarBase[] = [];

    roadData: AStarBase[] = [];

    ins:AStar=null;

    public  Ins():AStar{
        if(this.ins == null){
            this.ins = this;
        }
        return this.ins;
    }


    /**
     * 初始化地图数据
     */
    initMap(map: mapBase[]) {
        this.roadData = [];
        for (let i = 0; i < map.length; i++) {
            this.roadData.push(new AStarBase(map[i].x, map[i].x, map[i].open))
        }
    }


    /**
     * 查找路径
     * @param map   地图数据
     * @param startX  开始X
     * @param startY  开始Y
     * @param endX    结束X
     * @param endY    结束Y
     */
    findPathByMap(map: mapBase[], startX: number, startY: number, endX: number, endY: number) {


        if (!map.some(v => v.x == startX && v.y == startY) || !map.some(v => v.x == endX && v.y == endY)) {
            console.log('开始或结束点不在地图中');
            return undefined;
        }
        if (map.find(v => v.x == startX && v.y == startY).open == 0 ||
            map.find(v => v.x == endX && v.y == endY).open == 0
        ) {
            console.log('开始或结束点不可通过');
            return undefined;
        }
        this.roadData = [];
        for (let i = 0; i < map.length; i++) {
            this.roadData.push(new AStarBase(map[i].x, map[i].y, map[i].open))
        }
        let startMap: AStarBase = this.roadData.find(v => v.x == startX && v.y == startY);

        this.openList.push(startMap);
        return  this.findPath(startX, startY, endX, endY);

    }


    /**
     * 查找路径(需要先初始化地图数据)
     * @param startX  开始X
     * @param startY  开始Y
     * @param endX    结束X
     * @param endY    结束Y
     */
    findPathBySelf( startX: number, startY: number, endX: number, endY: number) {


        if (!this.roadData.some(v => v.x == startX && v.y == startY) || !this.roadData.some(v => v.x == endX && v.y == endY)) {
            console.log('开始或结束点不在地图中');
            return undefined;
        }
        if (this.roadData.find(v => v.x == startX && v.y == startY).type == 0 ||
            this.roadData.find(v => v.x == endX && v.y == endY).type == 0
        ) {
            console.log('开始或结束点不可通过');
            return undefined;
        }
        this.roadData = [];
        for (let i = 0; i < this.roadData.length; i++) {
            this.roadData.push(new AStarBase(this.roadData[i].x, this.roadData[i].y, this.roadData[i].type))
        }
        let startMap: AStarBase = this.roadData.find(v => v.x == startX && v.y == startY);
        this.openList.push(startMap);
        return  this.findPath(startX, startY, endX, endY);

    }




    /**
     * 查找路径的递归
     * @param startX   开始X
     * @param startY   开始Y
     * @param endX   结束X
     * @param endY   结束Y
     */
    findPath(startX: number, startY: number, endX: number, endY: number) {
        let startMap: AStarBase = this.roadData.find(v => v.x == startX && v.y == startY);
        let endMap: AStarBase = this.roadData.find(v => v.x == endX && v.y == endY);

        //  上下左右四个方向检测
        for (let i = -1; i < 2; i ++) {
            for (let j = -1; j < 2; j ++) {
                // 斜角的剔除 当可以计算斜角的时候去除这个判定
                if(Math.abs(i) ==1 && Math.abs(j) == 1){
                    continue;
                }
                // 去除自身
                if(Math.abs(i) ==0 && Math.abs(j) == 0){
                    continue;
                }
                let cx = startX + i;
                let cy = startY + j;
                // console.log(cx,cy)
                // 不在地图里
                if (!this.roadData.some(v => v.x == cx && v.y == cy)) {
                    continue;
                }
                // 障碍物不可行走
                if (this.roadData.find(v => v.x == cx && v.y == cy).type == 0) {
                    continue;
                }
                // 已经在开启列表了
                if (this.openList.some(v => v.x == cx && v.y == cy)) {
                    continue;
                }
                // 已经在关闭列表了
                if (this.closeList.some(v => v.x == cx && v.y == cy)) {
                    continue;
                }
                let curMap = this.roadData.find(v => v.x == cx && v.y == cy);
                // console.error(cx,cy)

                let g = startMap.G + 1;
                let h = Math.abs(endMap.x - cx) + Math.abs(endMap.y - cy);
                let f = g + h;
                curMap.G = g;
                curMap.H = h;
                curMap.F = f;
                curMap.parentNode = startMap;
                this.openList.push(curMap);
            }
        }
        // 如果开放列表为空，说明已经没有可走路径了
        if (this.openList.length <= 0) {
            return undefined;
        }

        // 排序，将总代价F由小到大排序
        this.openList.sort((a, b) => {
            return a.F - b.F;
        })
        // console.error('最小点：：：'+this.openList[0].x,this.openList[0].y)
        // 在关闭列表里放入代价最小的那个
        this.closeList.push(this.openList[0]);
        // 把最小的从开放列表里移除
        this.openList.splice(0, 1);
        // 如果最小的就是终点,return true
        if (this.closeList[this.closeList.length - 1].x == endX && this.closeList[this.closeList.length - 1].y == endY) {
            let path = [];
            let curMap = this.closeList[this.closeList.length - 1];
            // console.log(this.closeList);
            while (true) {
                path.unshift([curMap.x, curMap.y])

                if (curMap.parentNode == null) {
                    break;
                }
                // console.log(curMap);
                // console.log(path);
                curMap = curMap.parentNode;
            }
            return path;
        } else {
            // 不是,继续寻找
            return this.findPath(this.closeList[this.closeList.length - 1].x, this.closeList[this.closeList.length - 1].y, endX, endY)
        }
    }

}